package cn.aitrox.ry.service.anno.service.impl;

import cn.aitrox.ry.bean.CommonPageInfo;
import cn.aitrox.ry.bean.RespResult;
import cn.aitrox.ry.common.service.BaseService;
import cn.aitrox.ry.enumtype.RespCodeEnum;
import cn.aitrox.ry.service.anno.config.AnnoNacosProperties;
import cn.aitrox.ry.service.anno.dao.AnnoLesionResultDao;
import cn.aitrox.ry.service.anno.dao.AnnoResultDao;
import cn.aitrox.ry.service.anno.dao.TaskDao;
import cn.aitrox.ry.service.anno.dao.TaskSeriesDao;
import cn.aitrox.ry.service.anno.dao.UserTaskDao;
import cn.aitrox.ry.service.anno.dto.user.task.in.TaskJoinableSearchInDto;
import cn.aitrox.ry.service.anno.dto.user.task.in.UserTaskSearchInDto;
import cn.aitrox.ry.service.anno.dto.user.task.out.UserTaskItemDto;
import cn.aitrox.ry.service.anno.dto.user.task.series.in.UserTaskSeriesSearchInDto;
import cn.aitrox.ry.service.anno.dto.user.task.series.out.SeriesAnnoDto;
import cn.aitrox.ry.service.anno.dto.user.task.series.out.UserTaskSeriesDto;
import cn.aitrox.ry.service.anno.entity.AnnoResultEntity;
import cn.aitrox.ry.service.anno.entity.TaskEntity;
import cn.aitrox.ry.service.anno.entity.TaskSeriesEntity;
import cn.aitrox.ry.service.anno.entity.UserTaskEntity;
import cn.aitrox.ry.service.anno.enumtype.task.TaskStatusEnum;
import cn.aitrox.ry.service.anno.po.anno.lesion.result.ArIdAlrCountPo;
import cn.aitrox.ry.service.anno.po.task.TaskSeriesCountPo;
import cn.aitrox.ry.service.anno.po.user.task.UserTaskFindCondPo;
import cn.aitrox.ry.service.anno.service.UserTaskService;
import cn.aitrox.ry.util.ArrayUtil;
import com.github.pagehelper.PageInfo;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class UserTaskServiceImpl extends BaseService implements UserTaskService {

    @Autowired
    private UserTaskDao userTaskDao;

    @Autowired
    private TaskDao taskDao;

    @Autowired
    private TaskSeriesDao taskSeriesDao;

    @Autowired
    private AnnoResultDao annoResultDao;

    @Autowired
    private AnnoLesionResultDao alrDao;

    @Autowired
    private AnnoNacosProperties nacosProp;

    @Override
    public List<UserTaskItemDto> userDoingTaskList(Integer userId) {
        List<UserTaskEntity> userTaskEntities = userTaskDao.findByUserIdStatus(userId, TaskStatusEnum.NOT_START.getStatus(), TaskStatusEnum.PROCESSING.getStatus());
        if (ArrayUtil.isEmpty(userTaskEntities)) {
            return new ArrayList<>();
        }
        List<Integer> taskIds = userTaskEntities.stream().map(UserTaskEntity::getTaskId).distinct().collect(Collectors.toList());
        // 触发任务完成
        List<TaskEntity> taskEntities = taskDao.findByIds(taskIds.toArray(), null);
        List<Integer> endTaskIds = taskEntities.stream().filter(x -> TaskStatusEnum._isEnd(x.getStatus())).map(TaskEntity::getId).collect(Collectors.toList());
        if (!ArrayUtil.isEmpty(endTaskIds)) {
            List<Integer> userTaskIdList2End = userTaskEntities.stream().filter(x -> endTaskIds.contains(x.getTaskId())).map(UserTaskEntity::getId).collect(Collectors.toList());
            UserTaskEntity userTaskUpdate = new UserTaskEntity();
            userTaskUpdate.setStatus(TaskStatusEnum.COMPLETE.getStatus());
            userTaskUpdate.setEndTime(new Date());
            userTaskDao.updateByIdsSelective(userTaskUpdate, userTaskIdList2End.toArray());

            taskIds = taskIds.stream().filter(x -> !endTaskIds.contains(x)).collect(Collectors.toList());
        }
        if (ArrayUtil.isEmpty(taskIds)) {
            return new ArrayList<>();
        }
        return this.getUserTaskItems(taskIds, userId);
    }

    @Override
    public CommonPageInfo<UserTaskItemDto> search(Integer userId, UserTaskSearchInDto input) {
        UserTaskFindCondPo cond = new UserTaskFindCondPo(userId, null, null, input.getTaskStatus());
        if (StringUtils.isNotEmpty(input.getKeyword())) {
            if (NumberUtils.isDigits(input.getKeyword())) {
                try {
                    cond.setTaskId(Integer.parseInt(input.getKeyword()));
                } catch (NumberFormatException e) {
                }
            }
            String likeKeword = "%" + input.getKeyword() + "%";
            cond.setLikeTaskName(likeKeword);
        }
        PageInfo<TaskEntity> pageInfo = userTaskDao.searchTaskByCustom(input.getPage(), input.getPageSize(), cond);

        if (ArrayUtil.isEmpty(pageInfo.getList())) {
            return new CommonPageInfo(0, 0, null);
        }
        List<Integer> taskIds = pageInfo.getList().stream().map(TaskEntity::getId).distinct().collect(Collectors.toList());
        List<UserTaskItemDto> userTaskItemList = this.getUserTaskItems(taskIds, userId);
        return new CommonPageInfo(pageInfo.getTotal(), pageInfo.getPages(), userTaskItemList);
    }

    @Override
    public CommonPageInfo<UserTaskItemDto> joinableSearch(Integer userId, TaskJoinableSearchInDto input) {
        PageInfo<TaskEntity> pageInfo = taskDao.joinableSearch(input.getPage(), input.getPageSize(), input.getKeyword(), userId, TaskStatusEnum.PROCESSING.getStatus());
        if (ArrayUtil.isEmpty(pageInfo.getList())) {
            return new CommonPageInfo(0, 0, null);
        }
        List<Integer> taskIds = pageInfo.getList().stream().map(TaskEntity::getId).distinct().collect(Collectors.toList());
        List<UserTaskItemDto> userTaskItemList = this.getUserTaskItems(taskIds, userId);
        return new CommonPageInfo(pageInfo.getTotal(), pageInfo.getPages(), userTaskItemList);
    }

    @Transactional
    @Override
    public RespResult userTaskJoin(Integer userId, List<Integer> taskIds) {
        // 校验任务是否存在
        List<TaskEntity> taskEntities = taskDao.findByIds(taskIds.toArray(), null);
        for (Integer taskId : taskIds) {
            TaskEntity taskEntity = taskEntities.stream().filter(x -> x.getId().equals(taskId)).findAny().orElse(null);
            if (null == taskEntity) {
                return RespResult.error("任务ID不存在! ID: " + taskId);
            }
        }

        // 校验用户还可以领取几个任务
        List<UserTaskEntity> userDoingTaskList = userTaskDao.findByUserIdStatus(userId, TaskStatusEnum.NOT_START.getStatus(), TaskStatusEnum.PROCESSING.getStatus());
        if (taskIds.size() > (nacosProp.getTaskGetMaxCount() - userDoingTaskList.size())) {
            return RespResult.error("最多只能领取" + nacosProp.getTaskGetMaxCount() + "个任务");
        }

        // 校验用户是否已经领取过该任务
        List<UserTaskEntity> userTaskInDB = userTaskDao.findByUserIdTaskIds(userId, taskIds);
        UserTaskEntity userTaskGetted = userTaskInDB.stream().filter(x -> taskIds.contains(x.getTaskId())).findAny().orElse(null);
        if (null != userTaskGetted) {
            return RespResult.error("任务已领取! ID: " + userTaskGetted.getTaskId());
        }

        // 校验任务状态进行中
        TaskEntity taskEntity = taskEntities.stream().filter(x -> !TaskStatusEnum.isProcessing(x.getStatus())).findAny().orElse(null);
        if (null != taskEntity) {
            if (TaskStatusEnum.isNotStart(taskEntity.getStatus())) {
                return RespResult.error("任务未开始! ID: " + userTaskGetted.getTaskId());
            } else {
                return RespResult.error("任务已完成! ID: " + userTaskGetted.getTaskId());
            }
        }

        // 保存领取任务
        Date date = new Date();
        List<UserTaskEntity> userTaskEntityList = taskIds.stream().map(taskId -> {
            UserTaskEntity userTask = new UserTaskEntity(null, userId, taskId, TaskStatusEnum.NOT_START.getStatus(), date, null, null);
            return userTask;
        }).collect(Collectors.toList());

        userTaskDao.batchSave(userTaskEntityList);

        return RespResult.ok();
    }

    @Override
    public CommonPageInfo<UserTaskSeriesDto> userTaskSeriesSearch(UserTaskSeriesSearchInDto input) {
        String groupId = null;
        if (StringUtils.isNotEmpty(input.getSeriesUid())) {
            TaskSeriesEntity ts = taskSeriesDao.findByTaskSeriesUid(input.getTaskId(), input.getSeriesUid());
            if (null != ts) {
                groupId = ts.getGroupId();
            }
        }

        PageInfo<AnnoResultEntity> pageInfo = annoResultDao.searchUserGroupByTaskIdUserIdGroupIdOrderByStatus(input.getPage(), input.getPageSize(), input.getUserId(), input.getTaskId(), groupId);
        if (ArrayUtil.isEmpty(pageInfo.getList())) {
            return new CommonPageInfo<>(0, 0, null);
        }

        List<AnnoResultEntity> annoResultEntityList = annoResultDao.findByTaskIdUserIdGroupIds(input.getTaskId(), input.getUserId(), pageInfo.getList().stream().map(AnnoResultEntity::getGroupId).distinct().collect(Collectors.toList()));

        Map<String, List<AnnoResultEntity>> groupIdArListMap = annoResultEntityList.stream().collect(Collectors.groupingBy(AnnoResultEntity::getGroupId));

        List<Integer> arIds = annoResultEntityList.stream().map(AnnoResultEntity::getId).distinct().collect(Collectors.toList());

        List<ArIdAlrCountPo> arIdAlrCountPoList = alrDao.findCountGroupByArId(arIds);
        Map<Integer, Integer> arIdAlrCountMap = arIdAlrCountPoList.stream().collect(Collectors.toMap(ArIdAlrCountPo::getArId, ArIdAlrCountPo::getAlrCount));

        List<UserTaskSeriesDto> list = pageInfo.getList().stream().map(x -> {
            List<AnnoResultEntity> arListByGroup = groupIdArListMap.get(x.getGroupId());
            List<AnnoResultEntity> arList = arListByGroup.stream().filter(y -> y.getUserId().equals(x.getUserId())).collect(Collectors.toList());

            Integer sumAlrCount = arList.stream().mapToInt(arItem -> arIdAlrCountMap.get(arItem.getId()) == null ? 0 : arIdAlrCountMap.get(arItem.getId())).sum();
            List<SeriesAnnoDto> seriesAnnoList = arList.stream().map(y -> new SeriesAnnoDto(y.getId(), y.getSeriesUid(), arIdAlrCountMap.get(y.getId()) == null ? 0 : arIdAlrCountMap.get(y.getId()))).collect(Collectors.toList());

            AnnoResultEntity ar = arList.get(0);

            return new UserTaskSeriesDto(x.getGroupId(), sumAlrCount, ar.getSubmitTime(), ar.getStatus(), x.getUserId(), null, seriesAnnoList);
        }).collect(Collectors.toList());


        return new CommonPageInfo<>(pageInfo.getTotal(), pageInfo.getPages(), list);
    }

    @Override
    public RespResult<UserTaskItemDto> taskAnnoInfo(Integer taskId, Integer userId) {
        TaskEntity taskEntity = taskDao.findById(taskId);
        if (null == taskEntity) {
            return RespResult.secByError(RespCodeEnum.NOT_EXIST.getCode(), "任务不存在");
        }
        List<UserTaskItemDto> userTaskItems = this.getUserTaskItems(Arrays.asList(taskId), userId);
        return RespResult.ok(userTaskItems.get(0));
    }

    private List<UserTaskItemDto> getUserTaskItems(List<Integer> taskIds, Integer userId) {
        List<TaskEntity> taskEntities = taskDao.findByIds(taskIds.toArray(), null);
        if (ArrayUtil.isEmpty(taskEntities)) {
            return new ArrayList<>();
        }

        Map<Integer, TaskEntity> taskMap = taskEntities.stream().collect(Collectors.toMap(TaskEntity::getId, x -> x));

        List<TaskSeriesCountPo> seriesCountListByTs = taskSeriesDao.findTaskSeriesCountByTaskIds(taskIds);

        Map<Integer, Integer> seriesCountMapByTs = seriesCountListByTs.stream().collect(Collectors.toMap(TaskSeriesCountPo::getTaskId, TaskSeriesCountPo::getSeriesCount));

        List<TaskSeriesCountPo> seriesCountListByAr = annoResultDao.findTaskSeriesCountByTaskIdsUserId(taskIds, userId);
        Map<Integer, TaskSeriesCountPo> seriesCountMapByAr = seriesCountListByAr.stream().collect(Collectors.toMap(TaskSeriesCountPo::getTaskId, x -> x));

        Map<Integer, UserTaskEntity> userTaskMap = new HashMap<>();
        if (null != userId) {
            List<UserTaskEntity> userTaskEntityList = userTaskDao.findByUserIdTaskIds(userId, taskIds);
            userTaskMap = userTaskEntityList.stream().collect(Collectors.toMap(UserTaskEntity::getTaskId, x -> x, (k1, k2) -> k1));
        }

        Map<Integer, UserTaskEntity> finalUserTaskMap = userTaskMap;

        List<UserTaskItemDto> list = taskIds.stream().map(taskId -> {
            TaskEntity task = taskMap.get(taskId);
            Integer totalSeriesCount = seriesCountMapByTs.get(taskId) == null ? 0 : seriesCountMapByTs.get(taskId);
            TaskSeriesCountPo taskSeriesCountByAr = seriesCountMapByAr.get(taskId);

            Integer enableGetGroupCount = annoResultDao.findEnGetableGroupCount(taskId, userId, task.getCrossNum());

            UserTaskItemDto userTaskItem = new UserTaskItemDto();
            userTaskItem.setId(taskId);
            userTaskItem.setName(task.getName());
            userTaskItem.setLabel(task.getLabel());
            userTaskItem.setRemark(task.getRemark());
            userTaskItem.setStatus(task.getStatus());
            userTaskItem.setUserTaskStatus(null == finalUserTaskMap.get(taskId) ? null : finalUserTaskMap.get(taskId).getStatus());
            userTaskItem.setTaskStartDate(task.getBeginDate());
            userTaskItem.setTaskEndDate(task.getEndDate());
            userTaskItem.setTotalSeriesCount(totalSeriesCount);
            userTaskItem.setEnableGetSeriesCount(enableGetGroupCount);
            userTaskItem.setUserGettedSeriesCount(null == taskSeriesCountByAr ? 0 : taskSeriesCountByAr.getSeriesCount());
            userTaskItem.setUserUnSubmmitSeriesCount(null == taskSeriesCountByAr ? 0 : taskSeriesCountByAr.getUnSubmmitSeriesCount());
            userTaskItem.setImageLayout(task.getImageLayout());
            return userTaskItem;
        }).collect(Collectors.toList());

        return list;
    }
}
